#ifndef SYNTAX_PARSER_CPP
#define SYNTAX_PARSER_CPP

#include "syntax_parser.h"
#include <string.h>
#include "main_container.h"
#include "errors_warnings.h"

extern MainContainer * CompilerData;

ErrorEntry::ErrorEntry(int id, char * str) {
	this->id = id;
	this->str = new char[strlen(str) + 1];
	strcpy(this->str, str);
}

ErrorEntry::~ErrorEntry() {
	if(str != NULL)	{
		delete[] str;
		str = NULL;
	}
}


WarningEntry::WarningEntry(int id, char * str) {
	this->id = id;
	this->str = new char[strlen(str) + 1];
	strcpy(this->str, str);
}

WarningEntry::~WarningEntry() {
	if(str != NULL)	{
		delete[] str;
		str = NULL;
	}
}


Parser::Parser() {
	CurrentLexicPosition = 0;
}

Parser::~Parser() { }

void Parser::StartParsing() {

#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("StartParsing");
#endif

	SyntaxTreeNode * root = new SyntaxTreeNode();
	root->LexUnitID = -1;
	CompilerData->SyntaxTree->RootNode = root;
	Parse_ProgramBody(root);

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("StartParsing");
#endif
}

/* <program_body> ~ <gpp_file> <program_body>  
 {trace, endOfFile, using, simulation, namespace, enum, class, model, numeric, template, 
 public, private, start, process, int, float, bool, state, time,
 void, Facility, Storage, Queue, ADTable, Group, OutputStream,
 NAME, abstract, protected, virtual, override, static, final, const}
 <program_body> ~ endOfProject  {endOfProject}

 This 2 productions can be seen as 1 production:
 <program_body> ~  <gpp_file> <gpp_file> <gpp_file>... endOfProject 

 */
void Parser::Parse_ProgramBody(SyntaxTreeNode * parent) {

#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_ProgramBody");
#endif

	LexicUnit currentUnit;
	do	{

#ifdef TRACE
  CompilerData->LogTrace->ReportLine("Getting lexic unit.");
#endif

		// get current lexic unit
		currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);
		// if current lexic unit is in the select set of {using, simulation, namespace, enum, class,	
		// model, numeric, template, public, private, start, process, int, float, bool, state, time
		// void, Facility, Storage, Queue, ADTable, Group, OutputStream, abstract, protected
		//  virtual, override, static, final, const, NAME, endOfFile}
		if((currentUnit.type == LUTypeKeyword && (currentUnit.subtype == KeywordUsing || currentUnit.subtype == KeywordSimulation
			|| currentUnit.subtype == KeywordNamespace || currentUnit.subtype == KeywordEnum || currentUnit.subtype == KeywordClass
			|| currentUnit.subtype == KeywordModel || currentUnit.subtype == KeywordNumeric || currentUnit.subtype == KeywordTemplate
			|| currentUnit.subtype == KeywordPublic || currentUnit.subtype == KeywordPrivate || currentUnit.subtype == KeywordStart
			|| currentUnit.subtype == KeywordProcess || currentUnit.subtype == KeywordInt || currentUnit.subtype == KeywordFloat  
			|| currentUnit.subtype == KeywordBool || currentUnit.subtype == KeywordState || currentUnit.subtype == KeywordTime
			|| currentUnit.subtype == KeywordVoid || currentUnit.subtype == KeywordFacility || currentUnit.subtype == KeywordStorage
			|| currentUnit.subtype == KeywordQueue || currentUnit.subtype == KeywordADTable || currentUnit.subtype == KeywordGroup || currentUnit.subtype == KeywordTrace
			|| currentUnit.subtype == KeywordOutputStream || currentUnit.subtype == KeywordAbstract || currentUnit.subtype == KeywordProtected
			|| currentUnit.subtype == KeywordVirtual || currentUnit.subtype == KeywordOverride 
			|| currentUnit.subtype == KeywordStatic || currentUnit.subtype == KeywordFinal || currentUnit.subtype == KeywordConst))
			|| (currentUnit.type == LUTypeIdentifier || (currentUnit.type == LUTypeSpecialType && 
			currentUnit.subtype == endOfFile)))	{

#ifdef TRACE
			CompilerData->LogTrace->ReportLine("Recognized program body.");
#endif

			//	write type of production in this node
			parent->Production = Gr_ProgramBody__GppFile_ProgramBody;
			//	make GppFile node and add child node to a parent node		
			SyntaxTreeNode * newNode = new SyntaxTreeNode();
			parent->Nodes->Add(newNode);
			Parse_GppFile(newNode);
		}

		// if current lexic unit is endOfProject
		// <program_body> ~ endOfProject  {endOfProject}
		else if(currentUnit.type == LUTypeSpecialType && currentUnit.subtype == endOfProject)	{

#ifdef TRACE
			CompilerData->LogTrace->ReportLine("Recognized lexic unit End Of Project.");
#endif
			// do not write epsilon type of production in this node
			// because this is special kind of method where in production 
			// left nonterminal symbol
			// is the same the last right nonterminal symbol
			// <program_body> ~ <gpp_file> <program_body> 
			parent->Production = Gr_ProgramBody__GppFile_ProgramBody;
			//	make endOfProject node and add chiald node to a parent node		
			SyntaxTreeNode * newNode = new SyntaxTreeNode();
			// process lexic unit
			newNode->LexUnitID=this->CurrentLexicPosition;
			newNode->Production= Gr_Terminal;
			parent->Nodes->Add(newNode);
			//after each lexic unit is processed, increase CurrentLexicPosition
			this->CurrentLexicPosition++;
			break;
		}	else	{

#ifdef TRACE
	  CompilerData->LogTrace->ReportLine("Error. Recovery is to skip this lexic unit.");
#endif

			// REPORT ERROR
			CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_PROGRAM_BEGGINING);
			// This is recovoring from error
			this->CurrentLexicPosition++;
			break;
		}
	}
	while(1);

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_ProgramBody");
#endif
}

// <class_mods> ~ <class_mod> <class_mods>  {abstract, public, private, protected, final}
// <class_mods> ~ ? {template, class}
// This production is seen as: <class_mods> ~ <class_mods> <class_mods><class_mods>...epsilon
void Parser::Parse_ClassMods(SyntaxTreeNode * parent) {

#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_ClassMods");
#endif

	LexicUnit currentUnit;
	do	{
#ifdef TRACE
  CompilerData->LogTrace->ReportLine("Getting lexic unit.");
#endif

		// get current lexic unit
		currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);
		// if current lexic unit is in the select set of {abstract, public, private, protected, final} 
		if((currentUnit.type == LUTypeKeyword && (currentUnit.subtype == KeywordPublic || currentUnit.subtype == KeywordPrivate
			|| currentUnit.subtype == KeywordAbstract || currentUnit.subtype == KeywordProtected
			|| currentUnit.subtype == KeywordFinal)))	{

#ifdef TRACE
			CompilerData->LogTrace->ReportLine("Recognized class modifier.");
#endif

			//	write type of production in this node
			parent->Production = Gr_ClassMods__ClassMod_ClassMods;
			//	make ClassMod node and add child node to a parent node		
			SyntaxTreeNode * newNode = new SyntaxTreeNode();
			parent->Nodes->Add(newNode);
			Parse_ClassMod(newNode);
		}
		
		// if current lexic unit is in the select set of {template, class}
		else if ((currentUnit.type == LUTypeKeyword && (currentUnit.subtype == KeywordTemplate ||
				currentUnit.subtype == KeywordClass)) )	{
#ifdef TRACE
			CompilerData->LogTrace->ReportLine("Recognized something after the class modifiers (if any).");
#endif

			// this is epsilon part of production
			parent->Production = Gr_ClassMods__ClassMod_ClassMods;
			// new node is not needed
			break;
		}	else	{

#ifdef TRACE
	  CompilerData->LogTrace->ReportLine("Error. Recovery is to skip this lexic unit.");
#endif

			// ERROR has been found
			// REPORT ERROR
			CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_NO_CLASS_MODIFIERS_OR_CLASS);
			// This is recovoring from error
			this->CurrentLexicPosition++;
			break;
		}
	}
	while(1);

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_ClassMods");
#endif
}

//<into_expression> ~ into <expression>  {into}
//<into_expression> ~ ? {send, ;}
void Parser::Parse_IntoExpression(SyntaxTreeNode * parent) {

#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("Parse_IntoExpression");
#endif

	LexicUnit currentUnit;
	// get current lexic unit
	currentUnit = CompilerData->Units->GetLexicUnit(this->CurrentLexicPosition);
	if(currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordInto)	{

#ifdef TRACE
		CompilerData->LogTrace->ReportLine("Recognized keyword \"into\"");
#endif

		//	write type of production in this node
		parent->Production = Gr_IntoExpression__into_Expression;
		//	process terminal symbol 'into'	
		SyntaxTreeNode * newNode = new SyntaxTreeNode();
		parent->Nodes->Add(newNode);
		newNode->LexUnitID=this->CurrentLexicPosition;
		newNode->Production=Gr_Terminal;
		//increase counter
		CurrentLexicPosition++;
		//process nonterminal symbol <expression>
		SyntaxTreeNode * newNode2 = new SyntaxTreeNode();
		parent->Nodes->Add(newNode2);
		Parse_Expression(newNode2);
	}	else if((currentUnit.type == LUTypeKeyword && currentUnit.subtype == KeywordSend)
			||(currentUnit.type == LUTypeSpecialCharacter && currentUnit.subtype == SpecialCharacterSemicolon))	{

#ifdef TRACE
		CompilerData->LogTrace->ReportLine("Recognized keyword \"send\" or semicolon");
#endif

		//	write type of production in this node
		parent->Production = Gr_IntoExpression__epsilon;
		// there are no symbols on the right side of this production
		// so that all
	}	else	{

#ifdef TRACE
		CompilerData->LogTrace->ReportLine("Error. No recovery for now.");
#endif

			// ERROR has been found
			// REPORT ERROR
		CompilerData->ErrorWarningReport->ReportError(&currentUnit, ERR_SYN_KEYWORD_INTO_EXPECTED);
		// MAKE LOGICAL RECOVERING
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("Parse_IntoExpression");
#endif
}
#endif
